#include "StdAfx.h"
#include "PagedTreeBlock.h"

using namespace std;

namespace FEngine
{
	PagedTreeBlock::PagedTreeBlock(const Vector3& _minPoint, const Vector3& _maxPoint, float _maxViewDistance, float _impostorApparitionDistance)
	{
		mIsVIsible							= false;
		mAABB								= new AABB(_minPoint, _maxPoint);
		mMaximumViewDistance				= _maxViewDistance;
		mImpostorApparitionDistance			= _impostorApparitionDistance;
		
		if (mMaximumViewDistance < 0)
			mMaximumViewDistance = 0;
		if (mImpostorApparitionDistance < 0)
			mImpostorApparitionDistance = 0;
		if (mMaximumViewDistance < mImpostorApparitionDistance)
			mImpostorApparitionDistance = mImpostorApparitionDistance;
		
		mMaximumViewDistanceSquared			= mMaximumViewDistance * mMaximumViewDistance;
		mImpostorApparitionDistanceSquared	= mImpostorApparitionDistance * mImpostorApparitionDistance;
	}

	PagedTreeBlock::~PagedTreeBlock()
	{
		if (mAABB)
			delete mAABB;
		
		mVectorObjectInstance.clear();
		
		for (vector<QuadVertex*>::iterator it = mVectorImpostorInstance.begin(); it != mVectorImpostorInstance.end(); ++it)
		{
			delete (*it)->vertex1;
			delete (*it)->vertex2;
			delete (*it)->vertex3;
			delete (*it)->vertex4;
		}
	}
	
	void PagedTreeBlock::addObject(const Vector3& _pos, const Vector3& _rot, const Vector3& _sca, const Vector3& _impostorScale)
	{
		// Add the 3D object
		Matrix4 mat;

		Matrix4 matTranslation;
		D3DXMatrixTranslation(&matTranslation, _pos.x, _pos.y, _pos.z);
		
		Matrix4 matScale;
		D3DXMatrixScaling(&matScale, _sca.x, _sca.y, _sca.z);
		
		Matrix4 matRot;
		D3DXMatrixRotationYawPitchRoll(&matRot, _rot.z, _rot.x, _rot.y);
		
		D3DXMatrixMultiply(&mat, &matScale, &matRot);
		D3DXMatrixMultiply(&mat, &mat, &matTranslation);
		
		mVectorObjectInstance.push_back(mat);

		// Add the impostor
		QuadVertex* quad = new QuadVertex();
		quad->vertex1 = new FE_IMPOSTOR();
		quad->vertex1->POSITION				= Vector3(-1.0f * _sca.x * _impostorScale.x, -1.0f * _sca.y * _impostorScale.y, 0);
		quad->vertex1->IMPOSTOR_POSITION	= Vector3(0, 0, 0);
		quad->vertex1->TEXCOORD				= Vector2(0, 1.0f);
		quad->vertex1->ROTATION				= _rot.z;
		quad->vertex2 = new FE_IMPOSTOR();
		quad->vertex2->POSITION				= Vector3(-1.0f * _sca.x * _impostorScale.x, 1.0f * _sca.y * _impostorScale.y, 0);
		quad->vertex2->IMPOSTOR_POSITION	= Vector3(0, 0, 0);
		quad->vertex2->TEXCOORD				= Vector2(0, 0);
		quad->vertex1->ROTATION				= _rot.z;
		quad->vertex3 = new FE_IMPOSTOR();
		quad->vertex3->POSITION				= Vector3(1.0f * _sca.x * _impostorScale.x, 1.0f * _sca.y * _impostorScale.y, 0);
		quad->vertex3->IMPOSTOR_POSITION	= Vector3(0, 0, 0);
		quad->vertex3->TEXCOORD				= Vector2(1.0f, 0);
		quad->vertex1->ROTATION				= _rot.z;
		quad->vertex4 = new FE_IMPOSTOR();
		quad->vertex4->POSITION				= Vector3(1.0f * _sca.x * _impostorScale.x, -1.0f * _sca.y * _impostorScale.y, 0);
		quad->vertex4->IMPOSTOR_POSITION	= Vector3(0, 0, 0);
		quad->vertex4->TEXCOORD				= Vector2(1.0f, 1.0f);
		quad->vertex1->ROTATION				= _rot.z;

		// Set the wolrd position of the quad to the bottom
		Vector3 billboardPos = _pos;
		billboardPos.y += (quad->vertex2->POSITION.y - quad->vertex1->POSITION.y) / 2.0f;

		// Set the world position of each vertices
		quad->vertex1->IMPOSTOR_POSITION = billboardPos;
		quad->vertex2->IMPOSTOR_POSITION = billboardPos;
		quad->vertex3->IMPOSTOR_POSITION = billboardPos;
		quad->vertex4->IMPOSTOR_POSITION = billboardPos;

		mVectorImpostorInstance.push_back(quad);
	}

	void PagedTreeBlock::addObject(const Matrix4& _worldMat)
	{
		mVectorObjectInstance.push_back(_worldMat);
	}
	
	bool PagedTreeBlock::isVisible() const
	{
		return mIsVIsible;
	}

	int	PagedTreeBlock::getNumberObject() const
	{
		return mVectorObjectInstance.size();
	}

	int	PagedTreeBlock::getNumberVisibleObject() const
	{
		return mVectorVisibleObjectInstance.size();
	}

	int	PagedTreeBlock::getNumberImpostor() const
	{
		return mVectorImpostorInstance.size();
	}

	int	PagedTreeBlock::getNumberVisibleImpostor() const
	{
		return mVectorVisibleImpostorInstance.size();
	}
	
	void PagedTreeBlock::_computeFrustumCulling()
	{
		mIsVIsible = CoreEngine::getInstance()->getCamera()->isVisible(mAABB);
		
		mVectorVisibleObjectInstance.clear();
		mVectorVisibleImpostorInstance.clear();

		if (!mIsVIsible)
			return;

		// Check if the AABB is partially or totally visible
		Vector2 points[] =
		{
			Vector2(mAABB->getMinPoint().x, mAABB->getMinPoint().z),
			Vector2(mAABB->getMaxPoint().x, mAABB->getMaxPoint().z),
			Vector2(mAABB->getMinPoint().x, mAABB->getMaxPoint().z),
			Vector2(mAABB->getMaxPoint().x, mAABB->getMinPoint().z),
		};
		
		Vector2 camPos(CoreEngine::getInstance()->getCamera()->getPosition().x, CoreEngine::getInstance()->getCamera()->getPosition().z);
		

		// We check if the four point of the AABB box are inside the view range
		// Thats not an accurate way of doing a shpere / box intersection, but its fast
		// and we dont need right now to be very precise on this calcul
		int numberPointVisible = 0;

		// Check if the page is inside the view range
		for (int i = 0; i < 4; ++i)
		{
			if ((camPos.x - points[i].x) * (camPos.x - points[i].x) + (camPos.y - points[i].y) * (camPos.y - points[i].y) <= mMaximumViewDistanceSquared)
				numberPointVisible += 1;
		}

		// The block is outside the range of view
		if (numberPointVisible == 0)
			return;
		
		// Check if we have to draw the impostors
		if (mMaximumViewDistance == mImpostorApparitionDistance)
		{
			mVectorVisibleObjectInstance = mVectorObjectInstance;
			return;
		}

		// The block is inside the range of view, we need to now if we have to show objects or impostors
		numberPointVisible = 0;
		for (int i = 0; i < 4; ++i)
		{
			if ((camPos.x - points[i].x) * (camPos.x - points[i].x) + (camPos.y - points[i].y) * (camPos.y - points[i].y) <= mImpostorApparitionDistanceSquared)
				numberPointVisible += 1;
		}

		// The block is full of objects
		if (numberPointVisible == 0)
		{
			mVectorVisibleImpostorInstance = mVectorImpostorInstance;
			return;
		}

		// The block if full of impostors
		if (numberPointVisible == 4)
		{
			mVectorVisibleObjectInstance = mVectorObjectInstance;
			return;
		}

		else
		{
			// Test each obect to see if it is in the view range
			Vector2 pt;
			for (unsigned int n = 0; n < mVectorObjectInstance.size(); ++n)
			{
				pt = Vector2(mVectorImpostorInstance[n]->vertex1->IMPOSTOR_POSITION.x, mVectorImpostorInstance[n]->vertex1->IMPOSTOR_POSITION.z);
				if ((camPos.x - pt.x) * (camPos.x - pt.x) + (camPos.y - pt.y) * (camPos.y - pt.y) <= mImpostorApparitionDistanceSquared)
					mVectorVisibleObjectInstance.push_back(mVectorObjectInstance[n]);
				else
					mVectorVisibleImpostorInstance.push_back(mVectorImpostorInstance[n]);
			}
		}
	}
	
	void PagedTreeBlock::getVectorVisibleObjectInstance(std::vector<Matrix4>* _vectorObjectInstance)
	{
		if (mIsVIsible)
			for (vector<Matrix4>::iterator it = mVectorVisibleObjectInstance.begin(); it != mVectorVisibleObjectInstance.end(); ++it)
				_vectorObjectInstance->push_back(*it);
	}

	void PagedTreeBlock::getVectorVisibleImpostorInstance(std::vector<FE_IMPOSTOR*>* _vectorImpostorInstance)
	{
		if (mIsVIsible)
			for (vector<QuadVertex*>::iterator it = mVectorVisibleImpostorInstance.begin(); it != mVectorVisibleImpostorInstance.end(); ++it)
			{
				_vectorImpostorInstance->push_back((*it)->vertex1);
				_vectorImpostorInstance->push_back((*it)->vertex2);
				_vectorImpostorInstance->push_back((*it)->vertex3);
				_vectorImpostorInstance->push_back((*it)->vertex4);
			}
	}
	
	void PagedTreeBlock::update(double _elapsedTime)
	{
		_computeFrustumCulling();

		if (!mIsVIsible)
			return;
	}

} // namespace FEngine
